// ChildView.cpp : implementation of the CChildView class
//

#include "stdafx.h"
#include "lab.h"
#include "ChildView.h"
#include <cmath>

extern void Andy(DWORD,DWORD,bool);
extern void Haohan(DWORD,DWORD,bool);
extern void Jason(DWORD,DWORD,bool);
extern void Wang(DWORD,DWORD,bool);
extern void Danh(DWORD,DWORD,bool);
extern void Zach(DWORD,DWORD,bool);

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

using namespace std;

const double GR_PI = 3.1415926535897932384626433832795;

const GLdouble FLATWIDTH = 150;
const GLdouble FLATDEPTH = 150;

const GLfloat BLACK[] = {0.f, 0.f, 0.f, 1.0f};
const GLfloat  GREEN[] = {3/255.f, 111/255.f, 54/255.f, 1.f};
const GLfloat  RED[] = {1.f, 0.f, 0.f, 1.f};
const GLfloat  CYAN[] = {0.f, 1.f, 1.f, 1.f};
const GLfloat  WHITE[] = {0.7f, 0.7f, 0.7f, 1.f};
const GLfloat  WHITESEETHROUGH[] = {0.7f, 0.7f, 0.7f, 0.8f};
const GLfloat  BLUESEETHROUGH[] = {0.5f, 0.5f, 1.0f, 0.2f};
const GLfloat  GRAY[] = {0.6f, 0.6f, 0.6f, 1.f};
const GLfloat DIRT[] = {0.47f, 0.28f, 0.1f, 1.0f};

const GLfloat  AMBIENT[] = {0.2f, 0.2f, 0.2f, 1.0f};

const GLfloat  LIGHT0LOC[]    = {1.f, 0.5f, 1.f, 0.f};
const GLfloat  LIGHT0DCOLOR[] = {1.f, 1.f, 1.f, 1.f};
const GLfloat  LIGHT0SCOLOR[] = {1.f, 1.f, 1.f, 1.f};
const GLfloat  LIGHT1LOC[]    = {-2.f, 0.4f, 1.f, 0.f};
const GLfloat  LIGHT1DCOLOR[] = {0.5f, 0.5f, 0.5f, 1.f};
const GLfloat  LIGHT1SCOLOR[] = {1.f, 1.f, 1.f, 1.f};

const GLfloat  LIGHT2LOC[]    = {0.f, -10.f, -20.f, 0.f};
const GLfloat  LIGHT2DCOLOR[] = {1.f, 1.f, 1.f, 1.f};
const GLfloat  LIGHT2SCOLOR[] = {1.f, 1.f, 1.f, 1.f};


static const int X_SIZE = 38;
static const int Z_SIZE = 50;

static const bool collisionMap[] = {
// X----->
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // Z
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1, // ^
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1, // ^
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,0,0,1,1,1,1,1,1,0,0,1,0,0,1,
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,1,0,0,1,
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,1,0,0,1,
1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,0,0,1,0,0,1,
1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,1,0,0,1,
1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,1,0,0,1,
1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,
1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,
1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,
1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,1,
1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
1,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
1,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
1,0,1,0,0,1,1,1,1,0,0,0,1,1,1,1,1,1,0,0,0,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,
1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,
1,0,1,0,0,0,1,1,1,1,1,1,1,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,
1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,1,1,1,1,1,1,0,0,1,0,0,1,0,0,0,1,
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,1,0,0,0,1,
1,0,1,0,0,0,1,1,1,1,1,1,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,1,0,0,0,1,
1,0,1,0,0,0,1,0,0,0,0,0,0,1,0,0,0,1,1,1,1,1,1,1,1,0,0,1,0,0,0,0,0,1,0,0,0,1,
1,0,1,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,
1,0,1,0,0,0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,
1,1,1,0,0,0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,
1,0,0,0,0,0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
1,0,0,0,0,0,1,0,0,1,0,0,0,1,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
1,0,0,0,0,0,1,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,
1,0,0,1,1,1,1,1,1,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,1,
1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,1,1,1,1,1,0,0,1,1,1,1,1,
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,
1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,110,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,
1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,
1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,
1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,
1,0,0,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,1,
1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,1,
1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,1,
1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
};

static inline bool CollisionDetectXZ(const int currentX,
							  const int currentZ,
							  const bool * map,
							  const int stride /* length (X-wise) */,
							  const int offsetX = 0,
							  const int offsetZ = 0)
{
	return map[((currentZ + offsetZ) * stride) + (currentX + offsetX)];
}

void nurbsError(GLenum errorCode)
{
   const GLubyte *estring = gluErrorString(errorCode);
   AfxMessageBox((LPCTSTR)estring);
}


// pass caller-allocated array
static void RandPoisson1d(unsigned N, double minimumD, double * poisson, double maxBound)
{
	double p;
	bool valid;

    for (unsigned k = 0; k < N; ++k)
    {
        do {
            valid = true;
            // random on ([0,maxBound])
			
			p = maxBound * (double)rand() / (double)RAND_MAX;

            for (unsigned i = 0; i < k; i++) // check points so far
			{
                if (fabs(poisson[i]-p) < minimumD)
                {
                    valid = false;
                    break; // optimize
                }
            }
        } while (!valid);

        poisson[k] = p;
    }
}

static inline double randRainStartPos(double starting_point, double deviate)
{
	// deviate away from the starting_point by the 'deviate' value (e.g.,3 + 5 would yield 3 -2.5,+2.5)
	return starting_point + (-deviate/2.0 + (deviate * rand()/(double)RAND_MAX));
}


CChildView::CChildView()
{
	m_currentMarbleLocation.Set(1,-2,1); // starting location (1,5,1)
	m_camera.Set(100, 80, 150, 0, 0, 0, 0, 1, 0);

	//m_camera.Set(20, 10, 50, 0, 0, 0, 0, 1, 0);
	//m_camera.Set(20, 100, 0, 0, 0, 0, 0, 1, 0);
	//m_camera.Set(1*cos(90.0 * GR_PI/180.0), 200, 1, 0, 0, 0, 0, 1, 0);
	//m_camera.Set(1*cos(90.0 * GR_PI/180.0), 200, 1, 0, 0, 0, 0, 1, 0);
	//m_camera.DollyCamera(-2, 0, 0);
	//m_camera.Pan(-2);
	//m_camera.Tilt(6);
	// some decent camera settings:
	/*
Eye(0.594144,193.734997,18.496983)
Cen(0.578152,-0.870445,16.384648)
Up(0.000000,1.000000,0.000000)
>>>>>>> .r23


Eye(0.791070,200.467023,43.809667)
Cen(0.867078,-1.631978,24.231054)
Up(0.000000,1.000000,0.000000)


Eye(15.161181,192.057485,52.576392)
Cen(15.278004,-1.055726,23.088883)
Up(0.000000,1.000000,0.000000)


Eye(15.514537,195.703148,53.827658)
Cen(15.469398,-1.201793,24.036970)
Up(0.000000,1.000000,0.000000)
*/

	// FIXME: I know the camera's off, but I don't really know how to fix it
	// values below derived empirically
	m_camera.Set(15.514537,195.703148,53.827658,
				15.469398,-1.201793,24.036970,
				0.000000,1.000000,0.000000);

	SetDoubleBuffer(true);

	m_firstdraw = true;
	m_bigRigs = false;
	m_cheatCode = "bigrigs";

	m_spinAngle = 0;
	m_spinTimer = 0;

	m_timeElapsed = GetTickCount();

	for (int i=0;i<NUM_PARTICLES;i++)
	{
		particle_enabled[i] = true;
		particle_px[i] = 0;
		particle_py[i] = 0;
		particle_pz[i] = 0;
	}

	wind = CGrVector(0,0,0);

   m_seethrough = false;
   m_controlpoints = true;
   m_interpolateends = false;
   m_texturemap = true;
   m_winning = false;

   // We'll use this to indicate the currently selected block
   m_selection = -1;

   m_texture.LoadFile("pebbles.bmp");

   m_boxtexture.LoadFile("monster.bmp");

	m_firstdraw = true;

	for(int u=0;  u<UPOINTS;  u++)
	{
	  for(int v=0;  v<VPOINTS;  v++)
	  {
		  double x = GLfloat(u * FLATWIDTH / double(UPOINTS - 1));
		  double z = GLfloat((VPOINTS - v - 1) * FLATDEPTH / double(VPOINTS - 1));
		 // Initial control points
		 m_p[u][v][0] = x;
		 m_p[u][v][2] = z;
		 m_p[u][v][1] = pow((cos(x) + cos(z)),4) * 2;

		 	// Texture points
		 m_ptex[u][v][0] = float(u) / float((UPOINTS - 1)/10.0);
		 m_ptex[u][v][1] = float(v) / float((VPOINTS - 1)/10.0);
	  }
	}

	for(int u=4;  u<10;  u++)
	{
	  for(int v=4;  v<12;  v++)
	  {
		  double x = GLfloat(u * FLATWIDTH / double(UPOINTS - 1));
		  double z = GLfloat((VPOINTS - v - 1) * FLATDEPTH / double(VPOINTS - 1));
		 // Initial control points
		 m_p[u][v][0] = x;
		 m_p[u][v][2] = z;
		 m_p[u][v][1] = -2;
	  }
	}
	
}

CChildView::~CChildView()
{

}

BEGIN_MESSAGE_MAP(CChildView,COpenGLWnd )
	ON_COMMAND(ID_FILE_SAVEBMPFILE, OnFileSavebmpfile)
	ON_WM_TIMER()
	ON_WM_LBUTTONDOWN()
	ON_WM_MOUSEMOVE()
	ON_WM_RBUTTONDOWN()
	ON_WM_KEYDOWN()
	ON_WM_KEYUP()
	ON_COMMAND(ID_CAMERA_PAN, &CChildView::OnCameraPan)
	ON_UPDATE_COMMAND_UI(ID_CAMERA_PAN, &CChildView::OnUpdateCameraPan)
	ON_COMMAND(ID_CAMERA_PITCH32785, &CChildView::OnCameraPitch)
	ON_UPDATE_COMMAND_UI(ID_CAMERA_PITCH32785, &CChildView::OnUpdateCameraPitch)
	ON_COMMAND(ID_CAMERA_ROLL, &CChildView::OnCameraRoll)
	ON_UPDATE_COMMAND_UI(ID_CAMERA_ROLL, &CChildView::OnUpdateCameraRoll)
	ON_COMMAND(ID_CAMERA_DOLLYXY, &CChildView::OnCameraDollyxy)
	ON_UPDATE_COMMAND_UI(ID_CAMERA_DOLLYXY, &CChildView::OnUpdateCameraDollyxy)
	ON_COMMAND(ID_ANIMATE_SPINFERRISWHEEL, &CChildView::OnAnimateSpinferriswheel)
	ON_UPDATE_COMMAND_UI(ID_ANIMATE_SPINFERRISWHEEL, &CChildView::OnUpdateAnimateSpinferriswheel)
END_MESSAGE_MAP()

BOOL CChildView::PreCreateWindow(CREATESTRUCT& cs) 
{
	if (!COpenGLWnd::PreCreateWindow(cs))
		return FALSE;

	cs.dwExStyle |= WS_EX_CLIENTEDGE;
	cs.style &= ~WS_BORDER;
	cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS, 
		::LoadCursor(NULL, IDC_ARROW), HBRUSH(COLOR_WINDOW+1), NULL);

	return TRUE;
}


void CChildView::ParticleSystem(DWORD elapsed, DWORD actual)
{
	// update particle positions depending on tick
	/*for (int i=0;i<NUM_PARTICLES;i++)
	{
		particle_px[i] = 0;
		particle_py[i] = 0;
		particle_pz[i] = 0;
	}*/
	//double r_x=((rand() - RAND_MAX/2)/(double)RAND_MAX);
	//double r_y=((rand() - RAND_MAX/2)/(double)RAND_MAX);
	//double r_z=((rand() - RAND_MAX/2)/(double)RAND_MAX);
	//wind += CGrVector(5*r_x,3*r_y,6*r_z) * elapsed/1000.0;

	const double w_x = 1*sin((double)(actual % 1000));
	const double w_y = 0.5*sin((double)(actual % 1000));
	const double w_z = 0.5*sin((double)(actual % 1000));
	wind.Set(w_x,w_y,w_z);

	glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, WHITE);
	glMaterialfv(GL_FRONT, GL_SPECULAR, WHITE);

	glBegin(GL_LINES);

	const double span = 50.0;

	RandPoisson1d(NUM_PARTICLES, 0.01, particle_pz, span);
	for (int i=0;i<NUM_PARTICLES;i++)
	{
		//glVertex3d(0, 5*i+wind.Y(), 5+wind.Z());
		//glVertex3d(0, 5*i+wind.Y()+5, 5+i+wind.Z());

		const double rain_velocity = -0.03;
		/*double offset_y = (double)(actual % 500);
		double r_y=20.0*((rand() - RAND_MAX/2)/(double)RAND_MAX);
		double actual_y = (500.0-offset_y)/70.0 + r_y;*/

		double actual_y = (particle_py[i] += rain_velocity) + wind[1];
		//particle_pz[i] += 0.003;

		/*
		double offset_y = (double)(actual % 200);
		double r_y=20.0*((rand() - RAND_MAX/2)/(double)RAND_MAX);
		double actual_y = (200.0-offset_y)/20.0 + r_y;
		//glColor4d(rand() / (double)RAND_MAX, rand() / (double)RAND_MAX, rand() / (double)RAND_MAX, rand() / (double)RAND_MAX);
		*/
		
		const double rainHeight = 0.3;
		if (actual_y-rainHeight > 0)
		{
			glVertex3d(wind[0] + i*0.1, actual_y, wind[2]  -span/2.0+particle_pz[i]);
			glVertex3d(wind[0] + i*0.1, actual_y-rainHeight, wind[2]  -span/2.0+particle_pz[i]);
		}
		else
		{
			particle_py[i] = randRainStartPos(10.0, 15.0);
		}
	}
	glEnd();
}

void CChildView::OnGLDraw(CDC *pDC)
{
	// Enable depth test
	glEnable(GL_DEPTH_TEST);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	// Determine the screen size so we can determine the aspect ratio
	int width, height;
	GetSize(width, height);
	GLdouble aspectratio = GLdouble(width) / GLdouble(height);

	// Set the camera parameters
	gluPerspective(m_camera.FieldOfView(),       // Vertical field of view in degrees.
		aspectratio,   // The aspect ratio.
		20.,           // Near clipping
		1000.);         // Far clipping

	// Set the camera location
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	m_camera.gluLookAt();

	// Enable lighting
   glEnable(GL_LIGHTING);


   glLightModelfv(GL_LIGHT_MODEL_AMBIENT, WHITE);

   glEnable(GL_LIGHT0);
   glLightfv(GL_LIGHT0, GL_POSITION, LIGHT0LOC);
   glLightfv(GL_LIGHT0, GL_DIFFUSE, LIGHT0DCOLOR);
   glLightfv(GL_LIGHT0, GL_SPECULAR, LIGHT0SCOLOR);
   glLightfv(GL_LIGHT0, GL_AMBIENT, AMBIENT);

   glEnable(GL_LIGHT1);
   glLightfv(GL_LIGHT1, GL_POSITION, LIGHT1LOC);
   glLightfv(GL_LIGHT1, GL_DIFFUSE, LIGHT1DCOLOR);
   glLightfv(GL_LIGHT1, GL_SPECULAR, LIGHT1SCOLOR);

   glEnable(GL_LIGHT2);
   glLightfv(GL_LIGHT2, GL_POSITION, LIGHT2LOC);
   glLightfv(GL_LIGHT2, GL_DIFFUSE, LIGHT2DCOLOR);
   glLightfv(GL_LIGHT2, GL_SPECULAR, LIGHT2SCOLOR);

	// Clear the screen
   glClearColor(0.0f, 0.0f, 0.0f, 0.0f) ;
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glEnable(GL_AUTO_NORMAL);

	//TRACE("Eye(%lf,%lf,%lf)\n",	m_camera.EyeX(), 	m_camera.EyeY(), 	m_camera.EyeZ());
	//TRACE("Cen(%lf,%lf,%lf)\n",	m_camera.CenterX(), 	m_camera.CenterY(), 	m_camera.CenterZ());
	//TRACE("Up(%lf,%lf,%lf)\n",	m_camera.UpX(), m_camera.UpY(), m_camera.UpZ());



	glPushMatrix();

	glPushMatrix();
	//TRACE("marble location: (%lf,%lf,%lf)\n", m_currentMarbleLocation.X(), m_currentMarbleLocation.Y(), m_currentMarbleLocation.Z());
	//glTranslated(m_currentMarbleLocation.X(), m_currentMarbleLocation.Y(), m_currentMarbleLocation.Z());

	glTranslated(m_currentMarbleLocation.X(), -2, m_currentMarbleLocation.Z()); // board starts at (1,5,1) [y=5 always, x=1,z=1]

	/*const double ml_x = m_currentMarbleLocation.X();
	const double ml_y = m_currentMarbleLocation.Y();
	const double ml_z = m_currentMarbleLocation.Z();
	glTranslated(ml_x, ml_y, ml_z);*/

	glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, BLACK);
	glMaterialfv(GL_FRONT, GL_SPECULAR, BLACK);

	Box(1, 1, 1);

	glPopMatrix();
	//glTranslated(4,4,4);
	//static int callNum=0;
	//glColor3d((double)10*callNum / 255.0, (double)10*callNum / 255.0, (double)10*callNum / 255.0);
	//callNum++;
	
	DWORD currentTime = GetTickCount();
	const DWORD timeAdvanced = currentTime - m_timeElapsed;

	if(m_firstdraw)
	{
       // Create a font display list
       CFont font;
	   string test = "Arial";
		LPCTSTR lp=(LPCTSTR)test.c_str();

       font.CreateFont(25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, lp);
  
       CFont *oldfont = pDC->SelectObject(&font);
  
       m_arialDL = glGenLists(255);
  
       // create display lists for glyphs 0 through 255 with 0.1 extrusion 
       // and default deviation. 
       wglUseFontOutlines(pDC->m_hDC, 0, 255, m_arialDL, 0.0f, 0.1f, 
                WGL_FONT_POLYGONS, m_gmf); 
  
      pDC->SelectObject(oldfont);

		OnFirstDraw();
	}

	glPushMatrix();
	glTranslated(-25,0,0);
	ParticleSystem(timeAdvanced, currentTime);
	glPopMatrix();

	Andy(timeAdvanced, currentTime, m_bigRigs);

	glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, BLUESEETHROUGH);
	glMaterialfv(GL_FRONT, GL_SPECULAR, BLACK);
	Haohan(timeAdvanced, currentTime, m_bigRigs);

	Jason(timeAdvanced, currentTime, m_bigRigs);
	Danh(timeAdvanced, currentTime, m_bigRigs);
	Wang(timeAdvanced, currentTime, m_bigRigs);


	glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, GRAY);
	glMaterialfv(GL_FRONT, GL_SPECULAR, BLACK);

	Zach(timeAdvanced, currentTime, m_bigRigs);

	//TRACE("sizeof(collisionMap): %d\n", sizeof(collisionMap));
	//TRACE("(2,2): %d\n", CollisionDetectXZ(2, 2, collisionMap, X_SIZE, -1, -1 /* convert to 1-based index */));

	m_timeElapsed = currentTime;

	// render here


	glTranslated(25,0,19);
	ActualDraw(false);

	glPopMatrix();

	glPushMatrix();

	glTranslated(35, -1, 40);
	glRotated(-90., 0, 1, 0);

	Quad(5.0, 0.5, 5.0, m_boxtexture);

	glPopMatrix();

	if(!m_winning)
	{
		glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, GREEN);
		glMaterialfv(GL_FRONT, GL_SPECULAR, WHITE);
		
	}
	else
	{
		glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, RED);
		glMaterialfv(GL_FRONT, GL_SPECULAR, WHITE);
		m_winning = false;
	}
  
   glPushMatrix();
   glTranslated(5, 4, -20);
  
   glListBase(m_arialDL);
   glScaled(10,10,10);
   glCallLists(6, GL_UNSIGNED_BYTE, "CSE472"); 
   glFrontFace(GL_CCW); 
  
   glPopMatrix();

   	if(m_currentMarbleLocation.X() >= 30 && m_currentMarbleLocation.X()<= 35 && m_currentMarbleLocation.Z() >= 35 && m_currentMarbleLocation.Z() <= 40)
	{
		m_currentMarbleLocation.Set(1,-2,1);
		m_winning = true;
	}

	glPushMatrix();
   glTranslated(0, -10, -20);
  
   glListBase(m_arialDL);
   glScaled(5,5,5);
   glCallLists(13, GL_UNSIGNED_BYTE, "CHEAT BIGRIGS"); 
   glFrontFace(GL_CCW); 
  
   glPopMatrix();

	glFlush();
}

double Normal3dv(double *v)
{
	return sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
}

void CChildView::OnFileSavebmpfile() 
{
	OnSaveImage();	
}

void CChildView::OnTimer(UINT nIDEvent) 
{
	Invalidate();
	COpenGLWnd ::OnTimer(nIDEvent);
}

inline void Normalize(GLdouble *v)
{
	GLdouble len = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
	v[0] /= len;
	v[1] /= len;
	v[2] /= len;
}

void CChildView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	const double inc_amt = 1.0;
	CGrVector wouldBeLocation = m_currentMarbleLocation;

	switch (nChar)
	{
	case VK_LEFT:
		wouldBeLocation += CGrVector(-inc_amt,0,0);
		break;
	case VK_RIGHT:
		wouldBeLocation += CGrVector(inc_amt,0,0);
		break;
	case VK_DOWN:
		wouldBeLocation += CGrVector(0,0,inc_amt);
		break;
	case VK_UP:
		wouldBeLocation += CGrVector(0,0,-inc_amt);
		break;
	}

	if (m_cheatCode[m_pressedSoFar.size()] == nChar 
		||
		m_cheatCode[m_pressedSoFar.size()] == nChar+32 /* case problems*/)
	{
		TRACE("pressed: %c\n", nChar);
		// is this the cheat code?
		m_pressedSoFar.push_back(nChar);

		if (m_pressedSoFar.size() == m_cheatCode.length())
		{
			TRACE("Big Rigs mode toggled\n");
			// cheat code success!
			m_bigRigs = !m_bigRigs;
			m_pressedSoFar.clear(); // avoid segfault from typing another!!
		}
	}
	else
	{
		TRACE("Cheat code start over\n");
		// start all over
		m_pressedSoFar.clear();
	}

	if (m_bigRigs || !CollisionDetectXZ(wouldBeLocation.X(),
		wouldBeLocation.Z(),
		collisionMap,
		X_SIZE))
	{
		// valid location (or cheats on)
		m_currentMarbleLocation = wouldBeLocation;
		Invalidate();
	}
	else
	{
		// invalid location
	}
}

void CChildView::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	
}

void CChildView::OnLButtonDown(UINT nFlags, CPoint point)
{
	m_camera.MouseDown(point.x, point.y);

	COpenGLWnd ::OnLButtonDown(nFlags, point);
}


void CChildView::OnMouseMove(UINT nFlags, CPoint point)
{
	if( (nFlags & MK_LBUTTON)  || (nFlags & MK_RBUTTON) )
	{
		m_camera.MouseMove(point.x, point.y);
		Invalidate();
	}

//	m_currentMarbleLocation = m_camera.Eye();
//	m_currentMarbleLocation /= 2.0;

	COpenGLWnd::OnMouseMove(nFlags, point);
}


void CChildView::OnRButtonDown(UINT nFlags, CPoint point)
{
	m_camera.MouseDown(point.x, point.y, 2);

	COpenGLWnd::OnRButtonDown(nFlags, point);
}

void CChildView::OnCameraPan()
{
	m_camera.MouseMode(CGrCamera::PANTILT);
}

void CChildView::OnUpdateCameraPan(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(m_camera.MouseMode() == CGrCamera::PANTILT);
}

void CChildView::OnCameraPitch()
{
	m_camera.MouseMode(CGrCamera::PITCHYAW);
}

void CChildView::OnUpdateCameraPitch(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(m_camera.MouseMode() == CGrCamera::PITCHYAW);
}

void CChildView::OnCameraRoll()
{
	m_camera.MouseMode(CGrCamera::ROLLMOVE);
}

void CChildView::OnUpdateCameraRoll(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(m_camera.MouseMode() == CGrCamera::ROLLMOVE);
}

void CChildView::OnCameraDollyxy()
{
	m_camera.MouseMode(CGrCamera::DOLLYXY);
}

void CChildView::OnUpdateCameraDollyxy(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(m_camera.MouseMode() == CGrCamera::DOLLYXY);
}

void CChildView::OnAnimateSpinferriswheel()
{
	if(m_spinTimer == 0)
	{
		// Create the timer
		m_spinTimer = SetTimer(1, 1000.0/33.0, NULL);
	}
	else
	{
		// Destroy the timer
		KillTimer(m_spinTimer);
		m_spinTimer = 0;
	}
}

void CChildView::OnUpdateAnimateSpinferriswheel(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(m_spinTimer != 0);
}

void CChildView::Box(GLdouble p_x, GLdouble p_y, GLdouble p_z)
{
   GLdouble a[] = {0., 0., p_z};
   GLdouble b[] = {p_x, 0., p_z};
   GLdouble c[] = {p_x, p_y, p_z};
   GLdouble d[] = {0., p_y, p_z};
   GLdouble e[] = {0., 0., 0.};
   GLdouble f[] = {p_x, 0., 0.};
   GLdouble g[] = {p_x, p_y, 0.};
   GLdouble h[] = {0., p_y, 0.};

   // Front
   glBegin(GL_QUADS);
      glNormal3d(0, 0, 1);
      glVertex3dv(a);
      glVertex3dv(b);
      glVertex3dv(c);
      glVertex3dv(d);
   glEnd();

   // Right
   glBegin(GL_QUADS);
      glNormal3d(1, 0, 0);
      glVertex3dv(c);
      glVertex3dv(b);
      glVertex3dv(f);
      glVertex3dv(g);
   glEnd();

   // Back
   glBegin(GL_QUADS);
      glNormal3d(0, 0, -1);
      glVertex3dv(h);
      glVertex3dv(g);
      glVertex3dv(f);
      glVertex3dv(e);
   glEnd();

   // Left
   glBegin(GL_QUADS);
      glNormal3d(-1, 0, 0);
      glVertex3dv(d);
      glVertex3dv(h);
      glVertex3dv(e);
      glVertex3dv(a);
   glEnd();

   // Top
   glBegin(GL_QUADS);
      glNormal3d(0, 1, 0);
      glVertex3dv(d);
      glVertex3dv(c);
      glVertex3dv(g);
      glVertex3dv(h);
   glEnd();

   // Bottom
   glBegin(GL_QUADS);
      glNormal3d(0, -1, 0);
      glVertex3dv(e);
      glVertex3dv(f);
      glVertex3dv(b);
      glVertex3dv(a);
   glEnd();
}

void CChildView::OnFirstDraw()
{
   m_firstdraw = false;

   // Allocate a NURBS renderer object
   m_nurbs = gluNewNurbsRenderer();
   gluNurbsProperty(m_nurbs, GLU_SAMPLING_TOLERANCE, 25.0);
   gluNurbsProperty(m_nurbs, GLU_DISPLAY_MODE, GLU_FILL);
   gluNurbsCallback(m_nurbs, GLU_ERROR, (void (_stdcall *)(void))nurbsError);
}

void CChildView::ActualDraw(bool p_select)
{
   if(p_select)
   {
      glInitNames();
   }

   glTranslated(-FLATWIDTH / 2., 0., -FLATDEPTH / 2.);


   if(m_texturemap)
      glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, WHITESEETHROUGH);
   else
      glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, DIRT);

   glMaterialfv(GL_FRONT, GL_SPECULAR, WHITE);
   glMaterialf(GL_FRONT, GL_SHININESS, 120);

   if(!p_select)
   {
      if(m_seethrough)
      {
         glEnable(GL_BLEND);
         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
      }

      // The U direction knots
      GLfloat uknots[UPOINTS + 4];
      for(int u=0;  u<UPOINTS + 4;  u++)
      {
         if(!m_interpolateends)
         {
            uknots[u] = (float)u;
         }
         else
         {
            uknots[u] = float(u < 4 ? 0 : (u >= UPOINTS ? UPOINTS - 3 : u - 3));
         }
      }

      // The V direction knots
      GLfloat vknots[VPOINTS + 4];
      for(int v=0;  v<VPOINTS + 4;  v++)
      {
         if(!m_interpolateends)
         {
            vknots[v] = (float)v;
         }
         else
         {
            vknots[v] = float(v < 4 ? 0 : (v >= VPOINTS ? VPOINTS - 3 : v - 3));
         }
      }

      glEnable(GL_AUTO_NORMAL);
	  glEnable(GL_LIGHTING);
	  glEnable(GL_LIGHT0);

      if(m_texturemap)
      {
         glEnable(GL_TEXTURE_2D);
		 glColor3d(1, 1, 1);
         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
         glBindTexture(GL_TEXTURE_2D, m_texture.MipTexName());
      }

      gluBeginSurface(m_nurbs);

      gluNurbsSurface(m_nurbs,
                      UPOINTS + 4, uknots,
                      VPOINTS + 4, vknots,
                      VPOINTS * 3,
                      3,
                      &m_p[0][0][0],
                      4, 4,
                      GL_MAP2_VERTEX_3);

      if(m_texturemap)
      {
         gluNurbsSurface(m_nurbs,
                         UPOINTS + 4, uknots,
                         VPOINTS + 4, vknots,
                         VPOINTS * 2,
                         2,
                         &m_ptex[0][0][0],
                         4, 4,
                         GL_MAP2_TEXTURE_COORD_2);
      }

      gluEndSurface(m_nurbs);

      glDisable(GL_TEXTURE_2D);

      glDisable(GL_AUTO_NORMAL);
      glDisable(GL_BLEND);
   }


}


void CChildView::Quad(GLdouble p_x, GLdouble p_y, GLdouble p_z, CTexture tex)
{
	//CTexture tex;

	//tex.LoadFile(L"monster");

	GLdouble c[] = {p_x, p_y, p_z};
	GLdouble d[] = {0., p_y, p_z};
	GLdouble g[] = {p_x, p_y, 0.};
	GLdouble h[] = {0., p_y, 0.};

	glEnable(GL_TEXTURE_2D);
	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

	glBindTexture(GL_TEXTURE_2D, tex.TexName());

	glBegin(GL_QUADS);
	glNormal3d(0, 1, 0);

	glVertex3dv(d);
	glTexCoord2f(0, 0);

	glVertex3dv(c);
	glTexCoord2f(1, 0);

	glVertex3dv(g);
	glTexCoord2f(1, 1);

	glVertex3dv(h);
	glTexCoord2f(0, 1);
	glEnd();

	glDisable(GL_TEXTURE_2D);

   

}